unit uDebugEngine;

interface

uses
  Windows, Classes, uProtocol;
  
type
  PThreadMsgNode = ^TThreadMsgNode;
  TThreadMsgNode = record
    DbgMsg: TDebugMessage;
    Next: PThreadMsgNode;
  end;
  
  TDebugEngine = class(TThread)
  private
    FThreadMsgCS: TRTLCriticalSection;
    FTopMsgNode: PThreadMsgNode;
    FWorkMsgNode: PThreadMsgNode;
    FBotMsgNode: PThreadMsgNode;
    FDebugWindow: THandle;
    FEvent: THandle;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;

    procedure Initalize;
    procedure AddThreadMsg(wIdent: Word; params: array of const; strMsg: string); overload;
    procedure AddThreadMsg(wIdent: Word; params: array of const; Buf: PChar=nil; BufLen: Integer=0); overload;
  end;

var
  G_DebugEngine: TDebugEngine;
  G_TotalByte: Integer = 0;
  G_SendBuff: array[0..1024*1024] of Byte;
implementation

uses
  Messages, SysUtils, madCodeHook, uInjectWinsock2;

var
  LoadLibraryANext: function (lpLibFileName: PChar): HMODULE; stdcall;
  LoadLibraryWNext: function (lpLibFileName: PWideChar): HMODULE; stdcall;
  CreateFileNext: function (lpFileName: PChar; dwDesiredAccess, dwShareMode: DWORD;
    lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD;
    hTemplateFile: THandle): THandle; stdcall;
  CreateFileWNext: function (lpFileName: PWideChar; dwDesiredAccess, dwShareMode: DWORD;
    lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD;
    hTemplateFile: THandle): THandle; stdcall;
  OpenFileNext: function (const lpFileName: LPCSTR; var lpReOpenBuff: TOFStruct; uStyle: UINT): HFILE; stdcall;
  _lopennext: function (const lpPathName: LPCSTR; iReadWrite: Integer): HFILE; stdcall;
  _lcreatNext: function (const lpPathName: LPCSTR; iAttribute: Integer): HFILE; stdcall;     
  
function LoadLibraryACallback(lpLibFileName: PChar): HMODULE; stdcall;
begin
  OutputDebugString(PChar('[InjectDll]load ' + lpLibFileName));
  result := LoadLibraryANext(lpLibFileName);
end;

function LoadLibraryWCallback(lpLibFileName: PWideChar): HMODULE; stdcall;
begin
  OutputDebugString(PChar('[InjectDll]load ' + lpLibFileName));
  result := LoadLibraryWNext(lpLibFileName);
end;

function CreateFileCallback(lpFileName: PChar; dwDesiredAccess, dwShareMode: DWORD;
  lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD;
  hTemplateFile: THandle): THandle; stdcall;
begin
  OutputDebugString(PChar('[CreateFileA] ' + lpFileName));
  result := CreateFileNext(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
      dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
end;

function CreateFileWCallback(lpFileName: PWideChar; dwDesiredAccess, dwShareMode: DWORD;
  lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD;
  hTemplateFile: THandle): THandle; stdcall;
begin
  result := CreateFileWNext(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
      dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
  if (lpFileName<>nil) and (not SameText(ExtractFileExt(lpFileName), '.dll')) then
    OutputDebugString(PChar('[CreateFileW] ' + lpFileName));    
end;

function OpenFileCallback(const lpFileName: LPCSTR; var lpReOpenBuff: TOFStruct; uStyle: UINT): HFILE; stdcall;
begin

end;

function _lopen(const lpPathName: LPCSTR; iReadWrite: Integer): HFILE; stdcall;
begin

end;

function _lcreat(const lpPathName: LPCSTR; iAttribute: Integer): HFILE; stdcall;
begin
end;
{ TDebugEngine }

constructor TDebugEngine.Create;
begin
  inherited Create(True);
  FTopMsgNode := nil;
  FWorkMsgNode := nil;
  FBotMsgNode := nil;
  FDebugWindow := 0;
  InitializeCriticalSection(FThreadMsgCS);
  FDebugWindow := FindWindow(nil, PChar(DEBUG_WINDOW_TITLE));
  if FDebugWindow=0 then
    OutputDebugString('[error FDebugWindow=0]');
  FEvent := CreateEvent(nil, false, false, nil);
end;

destructor TDebugEngine.Destroy;
begin
  if not Terminated then
  begin
    Terminate;
    if Suspended then Resume;
    SetEvent(FEvent);
    WaitFor;
  end;
  DeleteCriticalSection(FThreadMsgCS);
  inherited;
end;

procedure TDebugEngine.Execute;
var
  cpData: TCopyDataStruct;
  NextNode: PThreadMsgNode;
begin
  while not Terminated do
  begin
    if FWorkMsgNode=nil then
    try
      EnterCriticalSection(FThreadMsgCS);
      FWorkMsgNode := FTopMsgNode;
      FTopMsgNode := nil;
      FBotMsgNode := nil;
    finally
      LeaveCriticalSection(FThreadMsgCS);
    end;

    if Assigned(FWorkMsgNode) then
    with FWorkMsgNode^, FWorkMsgNode^.DbgMsg do
    begin
      Move(DbgMsg, G_SendBuff, Sizeof(TDebugMessage));
      if iBufLen>0 then
        Move(szBuf^, G_SendBuff[Sizeof(TDebugMessage)], iBufLen);
        
      FillChar(cpData, Sizeof(TCopyDataStruct), 0);
      cpData.cbData := Sizeof(TDebugMessage) + iBufLen;
      cpData.lpData := @G_SendBuff[0];
      SendMessage(FDebugWindow, WM_COPYDATA, 0, Integer(PChar(@cpData)));

      NextNode := Next;
      if iBufLen>0 then
        FreeMem(szBuf);
      Dispose(FWorkMsgNode);
      FWorkMsgNode := NextNode;
    end;
    WaitForSingleObject(FEvent, 10);
  end;
end;

procedure TDebugEngine.Initalize;
begin
  Resume;
  //HookAPI('kernel32.dll', 'LoadLibraryA', @LoadLibraryACallback, @LoadLibraryANext);
  //HookAPI('kernel32.dll', 'LoadLibraryW', @LoadLibraryWCallback, @LoadLibraryWNext);
  HookAPI('kernel32.dll', 'CreateFileW', @CreateFileWCallback, @CreateFileWNext);
  HookAPI('kernel32.dll', 'CreateFile', @CreateFileCallback, @CreateFileNext);
  HookWinsock2;
end;

procedure TDebugEngine.AddThreadMsg(wIdent: Word; params: array of const; strMsg: string);
begin
  AddThreadMsg(wIdent, params, PChar(strMsg), Length(strMsg));
end;

procedure TDebugEngine.AddThreadMsg(wIdent: Word; params: array of const; Buf: PChar=nil; BufLen: Integer=0);
var
  p: PThreadMsgNode;
  i: Integer;
  iNum, sNum: Integer;
begin
  New(p);
  FillChar(p^, SizeOf(TThreadMsgNode), 0);
  iNum := 0;
  sNum := 0;
  with p^, p^.DbgMsg do
  begin
    Ident := wIdent;
    Tick := GetTickCount;
    for i := Low(params) to High(params) do
    begin
      case params[i].VType of
      vtInteger:
        begin
          iParams[iNum] := params[i].VInteger;
          Inc(iNum);
        end;
      vtChar:
        begin
          sParams[sNum] := params[i].VChar;
          Inc(sNum);
        end;
      vtString:
        begin
          sParams[sNum] := PAnsiChar(params[i].VAnsiString);
          Inc(sNum);
        end;
      end;
    end;
    if BufLen>0 then
    begin
      GetMem(szBuf, BufLen);
      Move(Buf^, szBuf^, BufLen);
    end;
    iBufLen := BufLen;
    
    EnterCriticalSection(FThreadMsgCS);
    try
      if Assigned(FBotMsgNode) then
        FBotMsgNode.Next := p
      else
        FTopMsgNode := p;
      FBotMsgNode := p;
    finally
      LeaveCriticalSection(FThreadMsgCS);
    end;
  end;
end;

end.
